home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 022 / pemacs / line.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  18KB  |  476 lines

  1. /*
  2.  * The functions in this file are a general set of line management utilities.
  3.  * They are the only routines that touch the text. They also touch the buffer
  4.  * and window structures, to make sure that the necessary updating gets done.
  5.  * There are routines in this file that handle the kill buffer too. It isn't
  6.  * here for any good reason.
  7.  *
  8.  * Note that this code only updates the dot and mark values in the window list.
  9.  * Since all the code acts on the current window, the buffer that we are
  10.  * editing must be being displayed, which means that "b_nwnd" is non zero,
  11.  * which means that the dot and mark values in the buffer headers are nonsense.
  12.  */
  13.  
  14. #include        <stdio.h>
  15. #include        "ed.h"
  16.  
  17. #define NBLOCK  16                      /* Line block chunk size        */
  18. #define KBLOCK  256                     /* Kill buffer block size       */
  19.  
  20. char    *kbufp  = NULL;                 /* Kill buffer data             */
  21. int     kused   = 0;                    /* # of bytes used in KB        */
  22. int     ksize   = 0;                    /* # of bytes allocated in KB   */
  23.  
  24. /*
  25.  * This routine allocates a block of memory large enough to hold a LINE
  26.  * containing "used" characters. The block is always rounded up a bit. Return
  27.  * a pointer to the new block, or NULL if there isn't any memory left. Print a
  28.  * message in the message line if no space.
  29.  */
  30. LINE    *
  31. lalloc(used)
  32. register int    used;
  33. {
  34.         register LINE   *lp;
  35.         register int    size;
  36.  
  37.         size = (used+NBLOCK-1) & ~(NBLOCK-1);
  38.         if (size == 0)                          /* Assume that an empty */
  39.                 size = NBLOCK;                  /* line is for type-in. */
  40.         if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
  41.                 mlwrite("Cannot allocate %d bytes", size);
  42.                 return (NULL);
  43.         }
  44.         lp->l_size = size;
  45.         lp->l_used = used;
  46.         return (lp);
  47. }
  48.  
  49. /*
  50.  * Delete line "lp". Fix all of the links that might point at it (they are
  51.  * moved to offset 0 of the next line. Unlink the line from whatever buffer it
  52.  * might be in. Release the memory. The buffers are updated too; the magic
  53.  * conditions described in the above comments don't hold here.
  54.  */
  55. lfree(lp)
  56. register LINE   *lp;
  57. {
  58.         register BUFFER *bp;
  59.         register WINDOW *wp;
  60.  
  61.         wp = wheadp;
  62.         while (wp != NULL) {
  63.                 if (wp->w_linep == lp)
  64.                         wp->w_linep = lp->l_fp;
  65.                 if (wp->w_dotp  == lp) {
  66.                         wp->w_dotp  = lp->l_fp;
  67.                         wp->w_doto  = 0;
  68.                 }
  69.                 if (wp->w_markp == lp) {
  70.                         wp->w_markp = lp->l_fp;
  71.                         wp->w_marko = 0;
  72.                 }
  73.                 wp = wp->w_wndp;
  74.         }
  75.         bp = bheadp;
  76.         while (bp != NULL) {
  77.                 if (bp->b_nwnd == 0) {
  78.                         if (bp->b_dotp  == lp) {
  79.                                 bp->b_dotp = lp->l_fp;
  80.                                 bp->b_doto = 0;
  81.                         }
  82.                         if (bp->b_markp == lp) {
  83.                                 bp->b_markp = lp->l_fp;
  84.                                 bp->b_marko = 0;
  85.                         }
  86.                 }
  87.                 bp = bp->b_bufp;
  88.         }
  89.         lp->l_bp->l_fp = lp->l_fp;
  90.         lp->l_fp->l_bp = lp->l_bp;
  91.         free((char *) lp);
  92. }
  93.  
  94. /*
  95.  * This routine gets called when a character is changed in place in the current
  96.  * buffer. It updates all of the required flags in the buffer and window
  97.  * system. The flag used is passed as an argument; if the buffer is being
  98.  * displayed in more than 1 window we change EDIT t HARD. Set MODE if the
  99.  * mode line needs to be updated (the "*" has to be set).
  100.  */
  101. lchange(flag)
  102. register int    flag;
  103. {
  104.         register WINDOW *wp;
  105.  
  106.         if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
  107.                 flag = WFHARD;
  108.         if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
  109.                 flag |= WFMODE;                 /* update mode lines.   */
  110.                 curbp->b_flag |= BFCHG;
  111.         }
  112.         wp = wheadp;
  113.         while (wp != NULL) {
  114.                 if (wp->w_bufp == curbp)
  115.                         wp->w_flag |= flag;
  116.                 wp = wp->w_wndp;
  117.         }
  118. }
  119.  
  120. /* Insert a string at dot.  Uses linsert, below
  121.  */
  122. linsertstr( s)
  123. register char *s;
  124. {
  125.     int result;
  126.     for( result = TRUE; *s; s++)
  127.     if((result =linsert( 1, *s)) == FALSE) break;
  128.  
  129.     return( result);
  130. }
  131.  
  132. /*
  133.  * Insert "n" copies of the character "c" at the current location of dot. In
  134.  * the easy case all that happens is the text is stored in the line. In the
  135.  * hard case, the line has to be reallocated. When the window list is updated,
  136.  * take special care; I screwed it up once. You always update dot in the
  137.  * current window. You update mark, and a dot in another window, if it is
  138.  * greater than the place where you did the insert. Return TRUE if all is
  139.  * well, and FALSE on errors.
  140.  */
  141. linsert(n, c)
  142. {
  143.         register char   *cp1;
  144.         register char   *cp2;
  145.         register LINE   *lp1;
  146.         register LINE   *lp2;
  147.         register LINE   *lp3;
  148.         register int    doto;
  149.         register int    i;
  150.         register WINDOW *wp;
  151.  
  152.         lchange(WFEDIT);
  153.         lp1 = curwp->w_dotp;                    /* Current line         */
  154.         if (lp1 == curbp->b_linep) {            /* At the end: special  */
  155.                 if (curwp->w_doto != 0) {
  156.                         mlwrite("bug: linsert");
  157.                         return (FALSE);
  158.                 }
  159.                 if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
  160.                         return (FALSE);
  161.                 lp3 = lp1->l_bp;                /* Previous line        */
  162.                 lp3->l_fp = lp2;                /* Link in              */
  163.                 lp2->l_fp = lp1;
  164.                 lp1->l_bp = lp2;
  165.                 lp2->l_bp = lp3;
  166.                 for (i=0; i<n; ++i)
  167.                         lp2->l_text[i] = c;
  168.                 curwp->w_dotp = lp2;
  169.                 curwp->w_doto = n;
  170.                 return (TRUE);
  171.         }
  172.         doto = curwp->w_doto;                   /* Save for later.      */
  173.         if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
  174.                 if ((lp2=lalloc(lp1->l_used+n)) == NULL)
  175.                         return (FALSE);
  176.                 cp1 = &lp1->l_text[0];
  177.                 cp2 = &lp2->l_text[0];
  178.                 while (cp1 != &lp1->l_text[doto])
  179.                         *cp2++ = *cp1++;
  180.                 cp2 += n;
  181.                 while (cp1 != &lp1->l_text[lp1->l_used])
  182.                         *cp2++ = *cp1++;
  183.                 lp1->l_bp->l_fp = lp2;
  184.                 lp2->l_fp = lp1->l_fp;
  185.                 lp1->l_fp->l_bp = lp2;
  186.                 lp2->l_bp = lp1->l_bp;
  187.                 free((char *) lp1);
  188.         } else {                                /* Easy: in place       */
  189.                 lp2 = lp1;                      /* Pretend new line     */
  190.                 lp2->l_used += n;
  191.                 cp2 = &lp1->l_text[lp1->l_used];
  192.                 cp1 = cp2-n;
  193.                 while (cp1 != &lp1->l_text[doto])
  194.                         *--cp2 = *--cp1;
  195.         }
  196.         for (i=0; i<n; ++i)                     /* Add the characters   */
  197.                 lp2->l_text[doto+i] = c;
  198.         wp = wheadp;                            /* Update windows       */
  199.         while (wp != NULL) {
  200.                 if (wp->w_linep == lp1)
  201.                         wp->w_linep = lp2;
  202.                 if (wp->w_dotp == lp1) {
  203.                         wp->w_dotp = lp2;
  204.                         if (wp==curwp || wp->w_doto>doto)
  205.                                 wp->w_doto += n;
  206.                 }
  207.                 if (wp->w_markp == lp1) {
  208.                         wp->w_markp = lp2;
  209.                         if (wp->w_marko > doto)
  210.                                 wp->w_marko += n;
  211.                 }
  212.                 wp = wp->w_wndp;
  213.         }
  214.         return (TRUE);
  215. }
  216.  
  217. /*
  218.  * Insert a newline into the buffer at the current location of dot in the
  219.  * current window. The funny ass-backwards way it does things is not a botch;
  220.  * it just makes the last line in the file not a special case. Return TRUE if
  221.  * everything works out and FALSE on error (memory allocation failure). The
  222.  * update of dot and mark is a bit easier then in the above case, because the
  223.  * split forces more updating.
  224.  */
  225. lnewline()
  226. {
  227.         register char   *cp1;
  228.         register char   *cp2;
  229.         register LINE   *lp1;
  230.         register LINE   *lp2;
  231.         register int    doto;
  232.         register WINDOW *wp;
  233.  
  234.         lchange(WFHARD);
  235.         lp1  = curwp->w_dotp;                   /* Get the address and  */
  236.         doto = curwp->w_doto;                   /* offset of "."        */
  237.         if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
  238.                 return (FALSE);
  239.         cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
  240.         cp2 = &lp2->l_text[0];
  241.         while (cp1 != &lp1->l_text[doto])
  242.                 *cp2++ = *cp1++;
  243.         cp2 = &lp1->l_text[0];
  244.         while (cp1 != &lp1->l_text[lp1->l_used])
  245.                 *cp2++ = *cp1++;
  246.         lp1->l_used -= doto;
  247.         lp2->l_bp = lp1->l_bp;
  248.         lp1->l_bp = lp2;
  249.         lp2->l_bp->l_fp = lp2;
  250.         lp2->l_fp = lp1;
  251.         wp = wheadp;                            /* Windows              */
  252.         while (wp != NULL) {
  253.                 if (wp->w_linep == lp1)
  254.                         wp->w_linep = lp2;
  255.                 if (wp->w_dotp == lp1) {
  256.                         if (wp->w_doto < doto)
  257.                                 wp->w_dotp = lp2;
  258.                         else
  259.                                 wp->w_doto -= doto;
  260.                 }
  261.                 if (wp->w_markp == lp1) {
  262.                         if (wp->w_marko < doto)
  263.                                 wp->w_markp = lp2;
  264.                         else
  265.                                 wp->w_marko -= doto;
  266.                 }
  267.                 wp = wp->w_wndp;
  268.         }       
  269.         return (TRUE);
  270. }
  271.  
  272. /*
  273.  * This function deletes "n" bytes, starting at dot. It understands how do deal
  274.  * with end of lines, etc. It returns TRUE if all of the characters were
  275.  * deleted, and FALSE if they were not (because dot ran into the end of the
  276.  * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
  277.  */
  278. ldelete(n, kflag)
  279. {
  280.         register char   *cp1;
  281.         register char   *cp2;
  282.         register LINE   *dotp;
  283.         register int    doto;
  284.         register int    chunk;
  285.         register WINDOW *wp;
  286.  
  287.         while (n != 0) {
  288.                 dotp = curwp->w_dotp;
  289.                 doto = curwp->w_doto;
  290.                 if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
  291.                         return (FALSE);
  292.                 chunk = dotp->l_used-doto;      /* Size of chunk.       */
  293.                 if (chunk > n)
  294.                         chunk = n;
  295.                 if (chunk == 0) {               /* End of line, merge.  */
  296.                         lchange(WFHARD);
  297.                         if (ldelnewline() == FALSE
  298.                         || (kflag!=FALSE && kinsert('\n')==FALSE))
  299.                                 return (FALSE);
  300.                         --n;
  301.                         continue;
  302.                 }
  303.                 lchange(WFEDIT);
  304.                 cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
  305.                 cp2 = cp1 + chunk;
  306.                 if (kflag != FALSE) {           /* Kill?                */
  307.                         while (cp1 != cp2) {
  308.                                 if (kinsert(*cp1) == FALSE)
  309.                                         return (FALSE);
  310.                                 ++cp1;
  311.                         }
  312.                         cp1 = &dotp->l_text[doto];
  313.                 }
  314.                 while (cp2 != &dotp->l_text[dotp->l_used])
  315.                         *cp1++ = *cp2++;
  316.                 dotp->l_used -= chunk;
  317.                 wp = wheadp;                    /* Fix windows          */
  318.                 while (wp != NULL) {
  319.                         if (wp->w_dotp==dotp && wp->w_doto>=doto) {
  320.                                 wp->w_doto -= chunk;
  321.                                 if (wp->w_doto < doto)
  322.                                         wp->w_doto = doto;
  323.                         }       
  324.                         if (wp->w_markp==dotp && wp->w_marko>=doto) {
  325.                                 wp->w_marko -= chunk;
  326.                                 if (wp->w_marko < doto)
  327.                                         wp->w_marko = doto;
  328.                         }
  329.                         wp = wp->w_wndp;
  330.                 }
  331.                 n -= chunk;
  332.         }
  333.         return (TRUE);
  334. }
  335.  
  336. /*
  337.  * Delete a newline. Join the current line with the next line. If the next line
  338.  * is the magic header line always return TRUE; merging the last line with the
  339.  * header line can be thought of as always being a successful operation, even
  340.  * if nothing is done, and this makes the kill buffer work "right". Easy cases
  341.  * can be done by shuffling data around. Hard cases require that lines be moved
  342.  * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
  343.  * "ldelete" only.
  344.  */
  345. ldelnewline()
  346. {
  347.         register char   *cp1;
  348.         register char   *cp2;
  349.         register LINE   *lp1;
  350.         register LINE   *lp2;
  351.         register LINE   *lp3;
  352.         register WINDOW *wp;
  353.  
  354.         lp1 = curwp->w_dotp;
  355.         lp2 = lp1->l_fp;
  356.         if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
  357.                 if (lp1->l_used == 0)           /* Blank line.          */
  358.                         lfree(lp1);
  359.                 return (TRUE);
  360.         }
  361.         if (lp2->l_used <= lp1->l_size-lp1->l_used) {
  362.                 cp1 = &lp1->l_text[lp1->l_used];
  363.                 cp2 = &lp2->l_text[0];
  364.                 while (cp2 != &lp2->l_text[lp2->l_used])
  365.                         *cp1++ = *cp2++;
  366.                 wp = wheadp;
  367.                 while (wp != NULL) {
  368.                         if (wp->w_linep == lp2)
  369.                                 wp->w_linep = lp1;
  370.                         if (wp->w_dotp == lp2) {
  371.                                 wp->w_dotp  = lp1;
  372.                                 wp->w_doto += lp1->l_used;
  373.                         }
  374.                         if (wp->w_markp == lp2) {
  375.                                 wp->w_markp  = lp1;
  376.                                 wp->w_marko += lp1->l_used;
  377.                         }
  378.                         wp = wp->w_wndp;
  379.                 }               
  380.                 lp1->l_used += lp2->l_used;
  381.                 lp1->l_fp = lp2->l_fp;
  382.                 lp2->l_fp->l_bp = lp1;
  383.                 free((char *) lp2);
  384.                 return (TRUE);
  385.         }
  386.         if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
  387.                 return (FALSE);
  388.         cp1 = &lp1->l_text[0];
  389.         cp2 = &lp3->l_text[0];
  390.         while (cp1 != &lp1->l_text[lp1->l_used])
  391.                 *cp2++ = *cp1++;
  392.         cp1 = &lp2->l_text[0];
  393.         while (cp1 != &lp2->l_text[lp2->l_used])
  394.                 *cp2++ = *cp1++;
  395.         lp1->l_bp->l_fp = lp3;
  396.         lp3->l_fp = lp2->l_fp;
  397.         lp2->l_fp->l_bp = lp3;
  398.         lp3->l_bp = lp1->l_bp;
  399.         wp = wheadp;
  400.         while (wp != NULL) {
  401.                 if (wp->w_linep==lp1 || wp->w_linep==lp2)
  402.                         wp->w_linep = lp3;
  403.                 if (wp->w_dotp == lp1)
  404.                         wp->w_dotp  = lp3;
  405.                 else if (wp->w_dotp == lp2) {
  406.                         wp->w_dotp  = lp3;
  407.                         wp->w_doto += lp1->l_used;
  408.                 }
  409.                 if (wp->w_markp == lp1)
  410.                         wp->w_markp  = lp3;
  411.                 else if (wp->w_markp == lp2) {
  412.                         wp->w_markp  = lp3;
  413.                         wp->w_marko += lp1->l_used;
  414.                 }
  415.                 wp = wp->w_wndp;
  416.         }
  417.         free((char *) lp1);
  418.         free((char *) lp2);
  419.         return (TRUE);
  420. }
  421.  
  422. /*
  423.  * Delete all of the text saved in the kill buffer. Called by commands when a
  424.  * new kill context is being created. The kill buffer array is released, just
  425.  * in case the buffer has grown to immense size. No errors.
  426.  */
  427. kdelete()
  428. {
  429.         if (kbufp != NULL) {
  430.                 free((char *) kbufp);
  431.                 kbufp = NULL;
  432.                 kused = 0;
  433.                 ksize = 0;
  434.         }
  435. }
  436.  
  437. /*
  438.  * Insert a character to the kill buffer, enlarging the buffer if there isn't
  439.  * any room. Always grow the buffer in chunks, on the assumption that if you
  440.  * put something in the kill buffer you are going to put more stuff there too
  441.  * later. Return TRUE if all is well, and FALSE on errors.
  442.  */
  443. kinsert(c)
  444. {
  445.         register char   *nbufp;
  446.         register int    i;
  447.  
  448.         if (kused == ksize) {
  449.                 if ((nbufp=malloc(ksize+KBLOCK)) == NULL)
  450.                         return (FALSE);
  451.                 for (i=0; i<ksize; ++i)
  452.                         nbufp[i] = kbufp[i];
  453.                 if (kbufp != NULL)
  454.                         free((char *) kbufp);
  455.                 kbufp  = nbufp;
  456.                 ksize += KBLOCK;
  457.         }
  458.         kbufp[kused++] = c;
  459.         return (TRUE);
  460. }
  461.  
  462. /*
  463.  * This function gets characters from the kill buffer. If the character index
  464.  * "n" is off the end, it returns "-1". This lets the caller just scan along
  465.  * until it gets a "-1" back.
  466.  */
  467. kremove(n)
  468. {
  469.         if (n >= kused)
  470.                 return (-1);
  471.         else
  472.                 return (kbufp[n] & 0xFF);
  473. }
  474.  
  475.  
  476.